package com.kaibes.web.security.core;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import com.kaibes.web.api.ApiLinkData;
import com.kaibes.web.api.ApiLinkService;
import com.kaibes.web.security.config.SecurityProperties;

@Component
public class AuthorityVoter implements AccessDecisionVoter<Object>, InitializingBean {

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    private final Map<ApiLinkData, SecurityConfig> linkMap = new HashMap<>();

    @Autowired
    private SecurityProperties securityProperties;
    @Autowired
    private ApiLinkService apiLinkService;
    @Autowired(required = false)
    private CheckAuthority checkAuthority;

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    @Override
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        if (authentication == null) {
            return ACCESS_DENIED;
        }

        // 如果没有登录，会以匿名用户到达此处
        FilterInvocation fi = (FilterInvocation) object;
        String method = fi.getRequest().getMethod();
        String uri = fi.getRequestUrl();

        if (securityProperties.isClosed()) {
            return ACCESS_GRANTED;
        }

        // 装载需要的权限
        for (Map.Entry<ApiLinkData, SecurityConfig> entry : linkMap.entrySet()) {
            if (method.equals(entry.getKey().getMethod())) {
                for (String pattern : entry.getKey().getPatternList()) {
                    if (antPathMatcher.match(pattern, uri)) {
                        attributes.clear();
                        attributes.add(entry.getValue());
                        break;
                    }
                }
            }
        }

        int result = ACCESS_ABSTAIN;

        for (ConfigAttribute attribute : attributes) {
            if (attribute.getAttribute() == null) {
                continue;
            }
            if (this.supports(attribute)) {
                result = ACCESS_DENIED;

                if (checkAuthority.hasAuthority(fi.getRequest(), authentication, attribute.getAttribute())) {
                    return ACCESS_GRANTED;
                }
            }
        }

        return result;
    }

	
    @Override
	public void afterPropertiesSet() throws Exception {
		if (checkAuthority == null) {
            checkAuthority = new CheckAuthorityToken();
        }
        List<ApiLinkData> apiLinkDatas = apiLinkService.listApi(true);
        for (ApiLinkData apiLinkData : apiLinkDatas) {
            linkMap.put(apiLinkData, new SecurityConfig(apiLinkData.getName()));
        }
	}

}
